#include "CubeMesh.h"

#include <OgreMeshManager.h>
#include <OgreSubMesh.h>

CubeMesh::CubeMesh():
mManObject(0),
mSceneNode(0),
mVoxArray(0)
{
	
}

CubeMesh::~CubeMesh()
{

}

SceneNode* CubeMesh::init(String name, SceneManager *sceneMan, uint32 gridSizePower)
{
	mGridSizePower = gridSizePower;
	mGridSize = 1 << (gridSizePower * 3);

	mStrideY = 1 << gridSizePower;
	mStrideZ = 1 << (gridSizePower * 2);

	mGridXmask = mGridYmask = mGridZmask = 0;
	uint32 i;
	for (i = 0; i < gridSizePower; i++)
	{
		mGridXmask |= 1 << i;
		mGridYmask |= 1 << (i + gridSizePower);
		mGridZmask |= 1 << (i + gridSizePower * 2);
	}

	mVoxArray = new uint32[mGridSize];
	for (i = 0; i < mGridSize; i++)
		mVoxArray[i] = 0;

	mFirstUpdate = true;
	mManObject = new ManualObject(name + ".obj");

	mSceneNode = sceneMan->getRootSceneNode()->createChildSceneNode(name);
	mSceneNode->attachObject(mManObject);

#if 1
	MeshPtr mesh = MeshManager::getSingleton().createManual(name + ".msh", "General");
	mSubMeshes = new SubMesh* [6];
	for (uint32 subMeshNum = 0; subMeshNum < 6; subMeshNum++)
	{
		SubMesh* pSubMesh = mSubMeshes[subMeshNum] = mesh->createSubMesh();
		pSubMesh->vertexData = new VertexData();
		pSubMesh->vertexData->vertexCount = 0;
		VertexDeclaration* vdec = pSubMesh->vertexData->vertexDeclaration;
		size_t offset = 0;
		vdec->addElement(0, offset, VET_FLOAT3, VES_POSITION);
		offset += VertexElement::getTypeSize(VET_FLOAT3);
		vdec->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
		offset += VertexElement::getTypeSize(VET_FLOAT3);
		vdec->addElement(0, offset, VET_COLOUR, VES_DIFFUSE);
		offset += VertexElement::getTypeSize(VET_COLOUR);
		vdec->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES);
		offset += VertexElement::getTypeSize(VET_FLOAT2);
	}
#endif

	return mSceneNode;
}

void CubeMesh::setCube(uint32 x, uint32 y, uint32 z, uint32 val)
{
	mVoxArray[x + y * mStrideY + z * mStrideZ] = val;
}

void CubeMesh::updateMesh()
{
	static uint32 lastQuadCount = 0;

	if (mFirstUpdate)
	{
		mFirstUpdate = false;
		mManObject->setDynamic(true);
		mManObject->begin("white_cube");
	}
	else
	{
		mManObject->estimateVertexCount((lastQuadCount + 6) * 4);
		mManObject->estimateIndexCount((lastQuadCount + 6) * 6);
		mManObject->beginUpdate(0);
	}
	uint32 quadCount = 0;
	uint32 voxNum;
	Vector3 normal;
	ColourValue color;

	for (voxNum = 0; voxNum < mGridSize; voxNum++)
	{
		if ((mVoxArray[voxNum] & 0xFF000000) > 0)
		{
			color.setAsARGB(mVoxArray[voxNum] | 0xFF000000);
			float x = float(voxNum & mGridXmask);
			float y = float((voxNum & mGridYmask) >> mGridSizePower);
			float z = float((voxNum & mGridZmask) >> (mGridSizePower * 2));
			if ((x == 0) || ((mVoxArray[voxNum - 1] & 0xFF000000) == 0))
			{
				//left
				normal = Vector3(-1,0,0);
				mManObject->position(x,y,z);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y,z+1);		mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y+1,z+1);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y+1,z);		mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
			if ((x == mGridXmask) || ((mVoxArray[voxNum + 1] & 0xFF000000) == 0))
			{
				//right
				normal = Vector3(1,0,0);
				mManObject->position(x+1,y,z);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z);	mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z+1);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y,z+1);	mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
			if ((y == 0) || ((mVoxArray[voxNum - mStrideY] & 0xFF000000) == 0))
			{
				//bottom
				normal = Vector3(0,-1,0);
				mManObject->position(x,y,z);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y,z);		mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y,z+1);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y,z+1);		mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
			if ((y == mGridXmask) || ((mVoxArray[voxNum + mStrideY] & 0xFF000000) == 0))
			{
				//top
				normal = Vector3(0,1,0);
				mManObject->position(x,y+1,z);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y+1,z+1);	mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z+1);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z);	mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
			if ((z == 0) || ((mVoxArray[voxNum - mStrideZ] & 0xFF000000) == 0))
			{
				//back
				normal = Vector3(0,0,-1);
				mManObject->position(x,y,z);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y+1,z);		mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y,z);		mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
			if ((z == mGridXmask) || ((mVoxArray[voxNum + mStrideZ] & 0xFF000000) == 0))
			{
				//front
				normal = Vector3(0,0,1);
				mManObject->position(x,y,z+1);		mManObject->textureCoord(0,0);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y,z+1);	mManObject->textureCoord(0,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x+1,y+1,z+1);	mManObject->textureCoord(1,1);	mManObject->normal(normal);	mManObject->colour(color);
				mManObject->position(x,y+1,z+1);	mManObject->textureCoord(1,0);	mManObject->normal(normal);	mManObject->colour(color);
				quadCount++;
			}
		}
	}
	for (uint32 q = 0; q < quadCount; q++)
	{
		mManObject->quad(q*4, q*4+1, q*4+2, q*4+3);
	}

	mManObject->end();
	lastQuadCount = quadCount;
}